<script>
import { GlForm, GlButton, GlAlert } from '@gitlab/ui';
import * as Sentry from '@sentry/browser';
import { s__, __ } from '~/locale';
import { convertToGraphQLId } from '~/graphql_shared/utils';
import { TYPENAME_PROJECT } from '~/graphql_shared/constants';
import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import createVulnerabilityMutation from 'ee/security_dashboard/graphql/mutations/vulnerability_create.mutation.graphql';
import SectionDetails from './section_details.vue';
import SectionIdentifiers from './section_identifiers.vue';
import SectionName from './section_name.vue';
import SectionSolution from './section_solution.vue';

export default {
  name: 'NewVulnerabilityForm',
  components: {
    GlForm,
    GlButton,
    GlAlert,
    SectionDetails,
    SectionIdentifiers,
    SectionName,
    SectionSolution,
  },
  inject: ['projectId', 'vulnerabilityReportPath'],
  data() {
    return {
      form: {
        vulnerabilityName: '',
        vulnerabilityDesc: '',
        severity: '',
        status: '',
        detectionMethod: '',
        identifiers: [],
      },
      validation: {
        severity: null,
        status: null,
        name: null,
        identifiers: [],
      },
      submitting: false,
      errors: [],
    };
  },
  computed: {
    shouldShowAlert() {
      return this.errors.length > 0;
    },
  },
  watch: {
    shouldShowAlert(newValue) {
      if (newValue) {
        this.scrollTop();
      }
    },
  },
  methods: {
    scrollTop() {
      this.$nextTick(() => {
        window.scrollTo({ top: 0, behavior: 'smooth' });
      });
    },

    async submitForm() {
      this.errors = this.validateFormValues();

      if (this.errors.length > 0) {
        if (this.shouldShowAlert) {
          this.scrollTop();
        }

        return;
      }

      this.submitting = true;

      try {
        const { data } = await this.$apollo.mutate({
          mutation: createVulnerabilityMutation,
          variables: {
            input: {
              project: convertToGraphQLId(TYPENAME_PROJECT, this.projectId),
              name: this.form.vulnerabilityName,
              description: this.form.vulnerabilityDesc,
              severity: this.form.severity.toUpperCase(),
              state: this.form.status.toUpperCase(),
              identifiers: this.form.identifiers,
              solution: this.form.solution,
              // The scanner needs to be hardcoded because of two reasons:
              // 1. It's a required field in the backend.
              // 2. We expect that the manually created vulnerabilities are the ones that the scanners cannot catch.
              //    So most likely the scanner will be left out even if we present an option to choose which one.
              scanner: {
                id: 'gitlab-manual-vulnerability-report',
                name: 'manually-created-vulnerability',
                url: 'https://gitlab.com',
                version: '1.0',
                vendor: {
                  name: 'GitLab',
                },
              },
            },
          },
        });

        if (data.vulnerabilityCreate.vulnerability?.vulnerabilityPath) {
          redirectTo(data.vulnerabilityCreate.vulnerability.vulnerabilityPath); // eslint-disable-line import/no-deprecated
          return;
        }

        if (data.vulnerabilityCreate.errors) {
          this.errors = data.vulnerabilityCreate.errors;
        } else {
          throw new Error(this.$options.i18n.submitError);
        }
      } catch (error) {
        this.errors = [this.$options.i18n.submitError];
        Sentry.captureException({ error, component: this.$options.name });
      }

      this.submitting = false;
    },

    validateFormValues() {
      const errors = [];
      const {
        vulnerabilityName,
        vulnerabilityState,
        vulnerabilitySeverity,
        vulnerabilityIdentifiers,
      } = this.$options.i18n.errors;

      this.validation = {};

      if (!this.form.vulnerabilityName) {
        this.validation.name = false;
        errors.push(vulnerabilityName);
      }

      if (!this.form.status) {
        this.validation.status = false;
        errors.push(vulnerabilityState);
      }

      if (!this.form.severity) {
        this.validation.severity = false;
        errors.push(vulnerabilitySeverity);
      }

      if (!this.form.identifiers?.length) {
        this.validation.identifiers = [{ identifierCode: false, identifierUrl: false }];
        errors.push(vulnerabilityIdentifiers);
      } else {
        this.validation.identifiers = [];
        this.validation.identifiers = this.form.identifiers.map((item) => ({
          identifierCode: Boolean(item.name),
          identifierUrl: Boolean(item.url),
        }));

        if (this.validation.identifiers.find((i) => !i.identifierUrl || !i.identifierCode)) {
          errors.push(vulnerabilityIdentifiers);
        }
      }

      return errors;
    },

    updateFormValues(values) {
      this.form = { ...this.form, ...values };

      // If there are previous errors, revalidate the form.
      if (this.errors.length) {
        this.validateFormValues();
      }
    },

    dismissAlert() {
      this.errors = [];
    },
  },
  i18n: {
    title: s__('VulnerabilityManagement|Add vulnerability finding'),
    cancel: __('Cancel'),
    submitVulnerability: s__('VulnerabilityManagement|Submit vulnerability'),
    submitError: s__('VulnerabilityManagement|Something went wrong while creating vulnerability'),
    description: s__(
      'VulnerabilityManagement|Manually add a vulnerability entry into the vulnerability report.',
    ),
    errors: {
      vulnerabilityName: s__('VulnerabilityManagement|Name is a required field'),
      vulnerabilitySeverity: s__('VulnerabilityManagement|Severity is a required field'),
      vulnerabilityState: s__('VulnerabilityManagement|Status is a required field'),
      vulnerabilityIdentifiers: s__(
        'VulnerabilityManagement|Identifier code and URL are required fields',
      ),
    },
  },
};
</script>

<template>
  <div data-testid="new-vulnerability-form">
    <header class="gl-my-4 gl-border-b-gray-100 gl-border-b-solid gl-border-b-1">
      <h2 class="gl-mt-0 gl-mb-3">
        {{ $options.i18n.title }}
      </h2>
      <p data-testid="page-description">
        {{ $options.i18n.description }}
      </p>
    </header>
    <gl-form autocomplete="off" @submit.prevent="submitForm">
      <gl-alert v-if="shouldShowAlert" variant="danger" dismissible @dismiss="dismissAlert">
        <ul v-if="errors.length > 1" class="gl-mb-0 gl-pl-5">
          <li v-for="error in errors" :key="error">{{ error }}</li>
        </ul>
        <span v-else>{{ errors[0] }}</span>
      </gl-alert>
      <div class="gl-p-4 gl-w-85p">
        <section-name :validation-state="validation" @change="updateFormValues" />
        <section-details :validation-state="validation" @change="updateFormValues" />
        <section-identifiers :validation-state="validation" @change="updateFormValues" />
        <section-solution @change="updateFormValues" />
      </div>
      <div class="gl-mt-5 gl-pt-5 gl-border-t-gray-100 gl-border-t-solid gl-border-t-1">
        <gl-button
          type="submit"
          variant="confirm"
          class="js-no-auto-disable gl-mr-3"
          :loading="submitting"
          >{{ $options.i18n.submitVulnerability }}</gl-button
        >
        <gl-button
          type="button"
          class="js-no-auto-disable"
          :disabled="submitting"
          :href="vulnerabilityReportPath"
          >{{ $options.i18n.cancel }}</gl-button
        >
      </div>
    </gl-form>
  </div>
</template>
